<?php

namespace lc\alipay;

use lc\alipay\aop\AopClient;
use lc\alipay\aop\request\AlipayMarketingCampaignCashCreateRequest;
use lc\alipay\aop\request\AlipayMarketingCampaignCashDetailQueryRequest;
use lc\alipay\aop\request\AlipayMarketingCampaignCashStatusModifyRequest;
use lc\alipay\aop\request\AlipayMarketingCampaignCashTriggerRequest;
use lc\alipay\aop\request\AlipayMarketingCampaignDrawcampTriggerRequest;
use lc\alipay\aop\request\AlipayOpenAppMiniTemplatemessageSendRequest;
use lc\alipay\aop\request\AlipayOpenPublicMessageSingleSendRequest;
use lc\alipay\aop\request\AlipayOpenPublicMessageTotalSendRequest;
use lc\alipay\aop\request\AlipayOpenPublicUserFollowQueryRequest;
use lc\alipay\aop\request\AlipaySystemOauthTokenRequest;
use lc\alipay\aop\request\AlipayUserAlipaypointSendRequest;
use app\Auth;
use lc\alipay\aop\request\AlipayUserInfoShareRequest;
use lc\alipay\aop\request\AntfortuneMarketingCrowdWshopMatchRequest;
use think\facade\Db;

class Factory
{
    /**
     * @var AopClient 客户端
     */
    public $aop;

    /**
     * @desc    根据 APPID 构造不同的财富号|小程序调用客户端
     * @param   string  $appId  财富号|小程序 APPID
     */
    public function __construct(string $appId)
    {
        $this->aop                      = new AopClient();
        $this->aop->gatewayUrl          = 'https://openapi.alipay.com/gateway.do';
        $this->aop->appId               = $appId;
        // 开发者私钥（签名工具生成）
        $this->aop->rsaPrivateKey       = file_get_contents(__DIR__ . '/key/' . $appId . '_private_key.txt');
        // 支付宝公钥（支付宝平台获取）
        $this->aop->alipayrsaPublicKey  = file_get_contents(__DIR__ . '/key/' . $appId . '_public_key.txt');
        $this->aop->apiVersion          = '1.0';
        $this->aop->signType            = 'RSA2';
        $this->aop->postCharset         = 'UTF-8';
        $this->aop->format              = 'json';
    }

    /**
     * @desc    小程序授权
     * @param   string  $code
     * @param   string  $refreshToken
     * @return  array
     */
    public function oauth(string $code, $refreshToken = ''): array
    {
        $status = false;
        try {
            $request = new AlipaySystemOauthTokenRequest();
            $request->setCode($code);
            $request->setGrantType($refreshToken ? 'refresh_token' : 'authorization_code');
            $request->setRefreshToken($refreshToken);
            $result  = $this->aop->execute($request);
            $resNode = str_replace('.', '_', $request->getApiMethodName()) . '_response';

            if (!empty($result->$resNode)) {
                $logResponse = $result->$resNode;
                $response    = json_decode(json_encode($result->$resNode), true);
                $status      = true;
            } else {
                $logResponse = $result->error_response;
                $response    = $result->error_response->sub_code;
            }
            $this->logs($resNode, json_encode([]), json_encode(['code' => $code]), json_encode($logResponse, 256));
        } catch (\Exception $e) {
            $response = $e->getMessage();
        }
        return ['status' => $status, 'response' => $response];
    }

    /**
     * @desc    支付宝获取会员信息接口
     * @param   string  $accessToken
     * @return  array
     */
    public function getUserInfo(string $accessToken): array
    {
        $status = false;
        try {
            $request = new AlipayUserInfoShareRequest();
            $result  = $this->aop->execute($request, $accessToken);
            $resNode = str_replace('.', '_', $request->getApiMethodName()) . '_response';

            if (!empty($result->$resNode)) {
                $logResponse = $result->$resNode;
                $response    = json_decode(json_encode($result->$resNode), true);;
                $status      = true;
            } else {
                $logResponse = $result->error_response;
                $response    = $result->error_response->sub_code;
            }
            $this->logs($resNode, json_encode([]), json_encode(['accessToken' => $accessToken]), json_encode($logResponse, 256));
        } catch (\Exception $e) {
            $response = $e->getMessage();
        }
        return ['status' => $status, 'response' => $response];
    }

    /**
     * @desc    发送模板消息
     * @param   string  $bizContent
     * @return  array
     */
    public function sendMiniAppTemplateMessage(string $bizContent): array
    {
        $status = false;
        try {
            // alipay.open.app.mini.templatemessage.send
            $request = new AlipayOpenAppMiniTemplatemessageSendRequest();
            $request->setBizContent($bizContent);

            $result       = $this->aop->execute($request);
            $responseNode = str_replace('.', '_', $request->getApiMethodName()) . '_response';
            if (!empty($result->$responseNode) && $result->$responseNode->code == 10000) {
                // 发送成功
                $status   = true;
                $response = json_decode(json_encode($result->$responseNode), true);
            } else {
                $response = $result->$responseNode->sub_code;
            }
            $this->logs($responseNode, json_encode([]), $bizContent, json_encode($result->$responseNode, 256));
        } catch (\Exception $e) {
            $response = $e->getMessage();
        }
        return ['status' => $status, 'response' => $response];
    }

    /**
     * @desc    判断用户是否关注财富号
     * @param   string $userId
     * @return  array
     */
    public function isFollow(string $userId): array
    {
        $status = false;
        try {
            $request    = new AlipayOpenPublicUserFollowQueryRequest();
            $bizContent = [
                'user_id' => $userId
            ];
            $request->setBizContent(json_encode($bizContent));
            $response     = $this->aop->execute($request);
            $result       = $response->alipay_open_public_user_follow_query_response;
            $responseNode = str_replace('.', '_', $request->getApiMethodName()) . '_response';

            $this->logs($responseNode, json_encode([]), json_encode($bizContent, 256), json_encode($result, 256));

            if ($result->code == 10000) {
                $status   = true;
                $response = $result->is_follow == 'T' ? 1 : 0;
            } else {
                $response =  0;
            }
        } catch (\Exception $e) {
            $response = $e->getMessage();
        }
        return ['status' => $status, 'response' => $response];
    }

    /**
     * @desc    消费红包触发
     * @param   string  $userId 用户id
     * @param   string  $crowd  活动信息
     * @return  array
     * @throws  \Exception
     */
    public function triggerXfCyjRedPack(string $userId, string $crowd): array
    {
        $status = false;
        try {
            $bizContent = ['user_id' => $userId, 'camp_id' => $crowd];
            $request    = new AlipayMarketingCampaignDrawcampTriggerRequest();
            $request->setBizContent(json_encode($bizContent));
            $result     = $this->aop->execute($request);

            $responseNode = str_replace('.', '_', $request->getApiMethodName()) . '_response';
            $this->logs($responseNode, json_encode([]), json_encode($bizContent, 256), json_encode($result->$responseNode, 256));

            $resultCode = $result->$responseNode->code;
            if (!empty($resultCode) && $resultCode == 10000) {
                if ($result->$responseNode->trigger_result) {
                    $status   = true;
                    $response = json_decode(json_encode($result->$responseNode), true);
                } else {
                    $response = $result->$responseNode->extend_field;
                }
            } else {
                $response = $result->$responseNode->sub_msg;
            }
        } catch (\Exception $e) {
            $response = $e->getMessage();
        }
        return ['status' => $status, 'response' => $response];
    }

    /**
     * @desc    发送集分宝
     * @param   string  $userId         用户ID
     * @param   string  $budgetCode     预算ID
     * @param   string  $partnerBizNo   订单ID
     * @param   int     $pointAmount    集分宝数量
     * @param   string  $remarks        发放备注
     * @return  array
     */
    public function sendAlipayPoint(string $userId, string $budgetCode, string $partnerBizNo, int $pointAmount, string $remarks): array
    {
        $status = false;
        try {
            $request    = new AlipayUserAlipaypointSendRequest();
            $bizContent =
                "{" .
                "\"user_id\":\"{$userId}\"," .
                "\"budget_code\":\"{$budgetCode}\"," .
                "\"partner_biz_no\":\"$partnerBizNo\"," .
                "\"point_amount\":{$pointAmount}," .
                "\"memo\":\"{$remarks}\"" .
                "  }";
            $request->setBizContent($bizContent);
            $result = $this->aop->execute($request);
            $responseNode = str_replace('.', '_', $request->getApiMethodName()) . '_response';

            // 判断请求结果
            $logResponse  = $result->$responseNode;
            if (!empty($logResponse->record_id)) {
                $response = $logResponse->record_id;
                $status   = true;
            } else {
                $response = $logResponse->sub_code;
            }

            // 记录接口日志
            $this->logs($responseNode, json_encode([]), $bizContent, json_encode($logResponse, 256));
        } catch (\Exception $e) {
            $response = $e->getMessage();
        }
        return ['status' => $status, 'response' => $response];
    }

    /**
     * @desc    创建现金红宝活动
     * @param   string      $couponName 红包名称
     * @param   string      $prizeType  现金红包的发放形式 fixed|random
     * @param   string      $totalMoney 活动发放的现金总金额
     * @param   string      $totalNum   红包发放个数
     * @param   string      $prizeMsg   活动文案
     * @param   mixed       $startTime  活动开始时间
     * @param   string      $endTime    活动结束时间
     * @param   string      $freqency   发送频率限制
     * @return  array
     */
    public function createCashRedPack(string $couponName, string $prizeType, string $totalMoney, string $totalNum, string $prizeMsg, $startTime, string $endTime, string $freqency): array
    {
        $status = false;
        try {
            // alipay.marketing.campaign.cash.create
            $request    = new AlipayMarketingCampaignCashCreateRequest();
            $bizContent =
                "{" .
                "\"coupon_name\":\"{$couponName}\"," .
                "\"prize_type\":\"{$prizeType}\"," .
                "\"total_money\":\"{$totalMoney}\"," .
                "\"total_num\":\"{$totalNum}\"," .
                "\"prize_msg\":\"{$prizeMsg}\"," .
                "\"start_time\":\"{$startTime}\"," .
                "\"end_time\":\"{$endTime}\"," .
                "\"send_freqency\":\"{$freqency}\"" .
                "}";
            $request->setBizContent($bizContent);
            $result = $this->aop->execute($request);
            $responseNode = str_replace('.', '_', $request->getApiMethodName()) . '_response';

            // 判断请求结果
            $logResponse  = $result->$responseNode;
            if (!empty($logResponse->code) && $logResponse->code == 10000) {
                $response = json_decode(json_encode($result->$responseNode), true);
                $status   = true;
            } else {
                $response = $logResponse->sub_code;
            }

            // 记录接口日志
            $this->logs($responseNode, json_encode([]), $bizContent, json_encode($logResponse, 256));
        } catch (\Exception $e) {
            $response = $e->getMessage();
        }
        return ['status' => $status, 'response' => $response];
    }

    /**
     * @desc    发红包接口
     * @param   string  $userId     支付宝用户ID
     * @param   string  $crowdNo    现金红包活动号
     * @param   string  $outBizNo   外部业务号
     * @param   int     $orderPrice 此字段如果传入金额，就忽略prize_type算法，按照传入的金额发奖
     * @param   string  $loginId    用户登录账号名：邮箱或手机号
     * @return  array
     */
    public function triggerCashRedPack(string $userId, string $crowdNo, string $outBizNo, int $orderPrice = 0, string $loginId = ''): array
    {
        $status = false;
        try {
            // alipay.marketing.campaign.cash.trigger
            $request    = new AlipayMarketingCampaignCashTriggerRequest();
            $bizContent =
                "{" .
                "\"user_id\":\"{$userId}\"," .
                "\"crowd_no\":\"{$crowdNo}\"," .
                "\"login_id\":\"{$loginId}\"," .
                "\"order_price\":\"{$orderPrice}\"," .
                "\"out_biz_no\":\"{$outBizNo}\"," .
                "}";
            $request->setBizContent($bizContent);
            $result = $this->aop->execute($request);
            $responseNode = str_replace('.', '_', $request->getApiMethodName()) . '_response';

            // 判断请求结果
            $logResponse  = $result->$responseNode;
            if (!empty($logResponse->code) && $logResponse->code == 10000) {
                $response = json_decode(json_encode($result->$responseNode), true);
                $status   = true;
            } else {
                $response = $logResponse->sub_code;
            }

            // 记录接口日志
            $this->logs($responseNode, json_encode([]), $bizContent, json_encode($logResponse, 256));
        } catch (\Exception $e) {
            $response = $e->getMessage();
        }
        return ['status' => $status, 'response' => $response];
    }

    /**
     * @desc    更改现金活动状态
     * @param   string $crowdNo     现金红包活动号
     * @param   string $campStatus  修改后的活动状态 PAUSE|READY|CLOSED
     * @return  array
     */
    public function modifyCashRedPack(string $crowdNo, string $campStatus): array
    {
        $status = false;
        try {
            // alipay.marketing.campaign.cash.status.modify
            $request    = new AlipayMarketingCampaignCashStatusModifyRequest();
            $bizContent =
                "{" .
                "\"crowd_no\":\"{$crowdNo}\"," .
                "\"camp_status\":\"{$campStatus}\"," .
                "}";
            $request->setBizContent($bizContent);
            $result = $this->aop->execute($request);
            $responseNode = str_replace('.', '_', $request->getApiMethodName()) . '_response';

            // 判断请求结果
            $logResponse  = $result->$responseNode;
            if (!empty($logResponse->code) && $logResponse->code == 10000) {
                $response = json_decode(json_encode($result->$responseNode), true);
                $status   = true;
            } else {
                $response = $logResponse->sub_code;
            }

            // 记录接口日志
            $this->logs($responseNode, json_encode([]), $bizContent, json_encode($logResponse, 256));
        } catch (\Exception $e) {
            $response = $e->getMessage();
        }
        return ['status' => $status, 'response' => $response];
    }

    /**
     * @desc    查询现金活动详情
     * @param   string  $crowdNo
     * @return  array
     */
    public function queryCashRedPack(string $crowdNo): array
    {
        $status = false;
        try {
            // alipay.marketing.campaign.cash.detail.query
            $request    = new AlipayMarketingCampaignCashDetailQueryRequest();
            $bizContent =
                "{" .
                "\"crowd_no\":\"{$crowdNo}\"" .
                "}";
            $request->setBizContent($bizContent);
            $result = $this->aop->execute($request);
            $responseNode = str_replace('.', '_', $request->getApiMethodName()) . '_response';

            // 判断请求结果
            $logResponse  = $result->$responseNode;
            if (!empty($logResponse->code) && $logResponse->code == 10000) {
                $response = json_decode(json_encode($result->$responseNode), true);
                $status   = true;
            } else {
                $response = $logResponse->sub_code;
            }

            // 记录接口日志
            $this->logs($responseNode, json_encode([]), $bizContent, json_encode($logResponse, 256));
        } catch (\Exception $e) {
            $response = $e->getMessage();
        }
        return ['status' => $status, 'response' => $response];
    }

    /**
     * @desc    人群匹配
     * @param   string $userId  支付宝用户ID
     * @param   string $crowd   人群ID
     * @return  array
     */
    public function wshopMatch(string $userId = '', string $crowd = '')
    {
        $status = false;
        try {
            // alipay.marketing.campaign.cash.detail.query
            $request    = new AntfortuneMarketingCrowdWshopMatchRequest();
            $bizContent =
                "{" .
                "\"user_id\":\"{$userId}\"," .
                "\"crowd_id\":\"{$crowd}\"" .
                "}";
            $request->setBizContent($bizContent);
            $result = $this->aop->execute($request);
            $responseNode = str_replace('.', '_', $request->getApiMethodName()) . '_response';

            // 判断请求结果
            $logResponse  = $result->$responseNode;
            if (!empty($logResponse->code) && $logResponse->code == 10000) {
                $response = json_decode(json_encode($result->$responseNode), true)['match_result'];
                $status   = true;
            } else {
                $response = $logResponse->sub_code;
            }

            // 记录接口日志
            $this->logs($responseNode, json_encode([]), $bizContent, json_encode($logResponse, 256));
        } catch (\Exception $e) {
            $response = $e->getMessage();
        }
        return ['status' => $status, 'response' => $response];
    }

    /**
     * @desc    群发消息，财富号|生活号普通消息
     * @param   array $data
     * @return  array
     */
    public function messageTotalSend(array $data)
    {
        $status = false;
        try {
            // alipay.marketing.campaign.cash.detail.query
            $request = new AlipayOpenPublicMessageTotalSendRequest();
            $request->setBizContent(json_encode($data, 256));
            $result = $this->aop->execute($request);
            $responseNode = str_replace('.', '_', $request->getApiMethodName()) . '_response';

            // 判断请求结果
            $logResponse  = $result->$responseNode;
            if (!empty($logResponse->code) && $logResponse->code == 10000) {
                $response = json_decode(json_encode($result->$responseNode), true);
                $status   = true;
            } else {
                $response = $logResponse->sub_code;
            }

            // 记录接口日志
            $this->logs($responseNode, json_encode([]), json_encode($data, 256), json_encode($logResponse, 256));
        } catch (\Exception $e) {
            $response = $e->getMessage();
        }
        return ['status' => $status, 'response' => $response];
    }

    /**
     * @desc    单发财富号模板消息
     * @param   array   $data
     * @return  array
     */
    public function sendSingleTemplateMessage(array $data)
    {
        $status = false;
        try {
            // alipay.open.app.mini.templatemessage.send
            $request = new AlipayOpenPublicMessageSingleSendRequest();
            $request->setBizContent(json_encode($data, 256));

            $result       = $this->aop->execute($request);
            $responseNode = str_replace('.', '_', $request->getApiMethodName()) . '_response';
            if (!empty($result->$responseNode) && $result->$responseNode->code == 10000) {
                // 发送成功
                $status   = true;
                $response = json_decode(json_encode($result->$responseNode), true);
            } else {
                $response = $result->$responseNode->sub_code;
            }
            $this->logs($responseNode, json_encode([]), json_encode($data, 256), json_encode($result->$responseNode, 256));
        } catch (\Exception $e) {
            $response = $e->getMessage();
        }
        return ['status' => $status, 'response' => $response];
    }

    /**
     * @desc    支付宝接口日志记录
     * @param   $name
     * @param   $params
     * @param   $body
     * @param   $response
     */
    private function logs($name, $params, $body, $response)
    {
         Db::name('ali_logs')
             ->insert([
                 'uid'          => Auth::$userId ?? (Auth::$adminId ?? 0),
                 'name'         => $name,
                 'param'        => $params,
                 'body'         => $body,
                 'response'     => $response,
                 'created'      => date('Y-m-d H:i:s')
             ]);
    }
}
